package org.summerframework.component.note.manager.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.summerframework.component.enums.UseEnum;
import org.summerframework.component.note.NoteResultCode;
import org.summerframework.component.note.code.service.NoteCodeRecordService;
import org.summerframework.component.note.code.service.dto.NoteCodeRecordDTO;
import org.summerframework.component.note.config.NoteProperties;
import org.summerframework.component.note.inform.service.NoteInformRecordService;
import org.summerframework.component.note.inform.service.dto.NoteInformRecordDTO;
import org.summerframework.component.note.manager.NoteManager;
import org.summerframework.component.note.manager.SendNoteService;
import org.summerframework.component.note.template.service.NoteTemplateService;
import org.summerframework.component.note.template.service.dto.NoteTemplateDTO;
import org.summerframework.core.base.result.DefaultResultCode;
import org.summerframework.core.exception.SystemException;
import org.summerframework.core.exception.WebException;
import org.summerframework.core.util.OptionalUtils;
import org.summerframework.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 石超
 * @version v1.0.0
 */
@Slf4j
public class NoteManagerSupport implements NoteManager {
    @Resource
    private NoteCodeRecordService noteCodeRecordService;

    @Resource
    private NoteInformRecordService noteInformRecordService;

    @Resource
    private NoteTemplateService noteTemplateService;

    private List<SendNoteService> sendNoteServices;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private NoteProperties noteProperties;

    @Autowired(required = false)
    public void setSendNoteServices(List<SendNoteService> sendNoteServices) {
        this.sendNoteServices = sendNoteServices;
    }

    @Override
    public String sendVerifyCode(String account, String templateCode, String ip) {
        final NoteTemplateDTO template = getTemplate(templateCode);
        String code = noteProperties.isEnableTest() ? noteProperties.getTestCode() : RandomUtil.createRandomCode(4, false);
        final NoteProperties.CheckSendProperties checkSendCode = noteProperties.getCode();
        Integer count = noteCodeRecordService.selectCountTodayByIp(ip);


        if (count > checkSendCode.getIp()) {// 同一ip,同一天只能请求50次
            log.error("该手机号请求过于频繁,请求信息={}, {}, {}, {}", account, templateCode, ip, code);
            throw new WebException(DefaultResultCode.ERROR_REQUEST_FREQUENT);
        }

        Integer[] countArr = noteCodeRecordService.selectCountByAccount(account);
        log.debug("发送验证码--手机号={} 1分钟内已发送={}, 1小时内已发送={}, 1天内已发送={}", account, countArr[0], countArr[1], countArr[2]);
        //对同一个手机号码发送短信验证码，1条/分钟，5条/小时，10条/天；
        if (countArr[0] > checkSendCode.getMinute() || countArr[1] > checkSendCode.getHour() || countArr[2] > checkSendCode.getDay()) {
            log.error("该手机号请求过于频繁,请求信息={}, {}, {}, {}", account, templateCode, ip, code);
            throw new WebException(DefaultResultCode.ERROR_REQUEST_FREQUENT);
        }

        // 查询此手机号最近一条记录,
        // 解决短时间内验证码未及时送达,多次点击获取验证码的问题.
        NoteCodeRecordDTO recentCode = noteCodeRecordService.selectByLastAccount(account, template.getId());

        if (recentCode != null && UseEnum.NOT_USE.isType(recentCode.getStatus())) {
            log.info("短时间多次点击，决定覆盖传入的验证码={}，最近未被使用验证码={}", code, recentCode.getCode());
            code = noteProperties.isEnableTest() ? noteProperties.getTestCode() : recentCode.getCode();
            recentCode.setRetry(recentCode.getRetry() + 1);
            recentCode.setUpdateTime(LocalDateTime.now());
            noteCodeRecordService.updateById(recentCode);
        } else {
            NoteCodeRecordDTO sendRec = new NoteCodeRecordDTO();
            sendRec.setIp(ip);
            sendRec.setAccount(account);
            sendRec.setCode(code);
            sendRec.setTemplateId(template.getId());
            sendRec.setStatus(UseEnum.NOT_USE.getCode());
            noteCodeRecordService.insert(sendRec);
        }

        Map<String, Object> param = new HashMap<>();
        param.put("code", code);
        getSendNoteService(template.getPlatformType()).sendNote(account, "86", template.getSignName(), template.getTemplateCode(), template.getTemplate(), param);

        return code;
    }

    @Override
    public void sendNote(String account, String templateCode, String ip, Map<String, Object> param) {
        final NoteTemplateDTO template = getTemplate(templateCode);
        // 验证是否符合发送条件(ip地址,发送时间间隔等)
        Integer count = noteInformRecordService.selectCountTodayByIp(ip);
        final NoteProperties.CheckSendProperties inform = noteProperties.getInform();

        if (count > inform.getIp()) {// 同一ip,同一天只能请求500次
            throw new WebException(DefaultResultCode.ERROR_REQUEST_FREQUENT);
        }

        //查询发送时间,流量控制
        Integer[] countArr = noteInformRecordService.selectCountByAccount(account);
        log.debug("发送通知--手机号={} 1分钟内已发送={}, 1小时内已发送={}, 1天内已发送={}", account, countArr[0], countArr[1], countArr[2]);

        //对同一个手机号码发送短信验证码，2条/分钟，5条/小时，10条/天；
        if (countArr[0] > inform.getMinute() || countArr[1] > inform.getHour() || countArr[2] > inform.getDay()) {
            log.error("该手机号请求过于频繁,请求信息={}, {}, {}, {}", account, templateCode, ip, param);
            throw new WebException(DefaultResultCode.ERROR_REQUEST_FREQUENT);
        }

        NoteInformRecordDTO sendRec = new NoteInformRecordDTO();
        sendRec.setIp(ip);
        sendRec.setAccount(account);
        try {
            sendRec.setParam(objectMapper.writeValueAsString(param));
        } catch (JsonProcessingException e) {
            log.warn("生成json异常", e);
            sendRec.setParam("");
        }
        sendRec.setTemplateId(template.getId());
        noteInformRecordService.insert(sendRec);

        getSendNoteService(template.getPlatformType()).sendNote(account, "86", template.getSignName(), template.getTemplateCode(), template.getTemplate(), param);
    }

    @Override
    public boolean verifyCode(String account, String templateCode, String code) {
        final NoteTemplateDTO template = getTemplate(templateCode);
        NoteCodeRecordDTO messageCodeRecord = noteCodeRecordService.selectByLastAccount(account, template.getId(), code);

        // 记录是否为空
        if (messageCodeRecord == null || messageCodeRecord.getCode() == null) {
            throw new WebException(NoteResultCode.CODE_INPUT_ERROR);
        } else if (!messageCodeRecord.getCode().equals(code)) {// 验证码是否正确
            throw new WebException(NoteResultCode.CODE_INPUT_ERROR);
        } else if (checkTime(templateCode, messageCodeRecord.getUpdateTime())) {// 验证码是否过期
            throw new WebException(NoteResultCode.CODE_PAST_DUE);
        } else if (messageCodeRecord.getStatus().equals(UseEnum.USE.getCode())) {// 是否已被使用
            throw new WebException(NoteResultCode.CODE_USE);
        } else {// 更改验证码状态
            messageCodeRecord.setStatus(UseEnum.USE.getCode());
            noteCodeRecordService.updateById(messageCodeRecord);
        }

        return true;
    }

    /**
     * 检测验证码是否过期
     *
     * @return true 验证码过期
     */
    private boolean checkTime(String templateCode, LocalDateTime date) {
        return Duration.between(LocalDateTime.now(), date).compareTo(noteProperties.getExpireTime(templateCode)) > 0;
    }

    private NoteTemplateDTO getTemplate(String code) {
        return OptionalUtils.get(noteTemplateService.selectByCode(code), "短信模板发送失败");
    }

    private SendNoteService getSendNoteService(Integer PlatformCode) {
        if (CollectionUtils.isNotEmpty(sendNoteServices)) {
            for (SendNoteService sendNoteService : sendNoteServices) {
                if (sendNoteService.isSupper(PlatformCode)) {
                    return sendNoteService;
                }
            }
        }

        throw new SystemException(DefaultResultCode.SYSTEM_ERROR, "短信发送配置失败");
    }
}
